package com.ctrlspace.core.compiler;

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.NoSuchElementException;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

import com.ctrlspace.model.JavaSourceFromString;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;

public class AutoComplete {
	public String getMethods(String code, int targetLineNumber, String variableName) throws IOException, ClassNotFoundException {
		String dataType = getDataType(code, targetLineNumber, variableName);
		String json = getMethodDetails(dataType);
		return json;
	}

	@SuppressWarnings("unchecked")
	private String getMethodDetails(String qualifiedName) throws ClassNotFoundException {
		String json = "";

		Class c = Class.forName(qualifiedName);
		Method[] methods = c.getMethods();

		// Bubble sort
		for (int i = 0; i < methods.length; i++) {
			for (int j = i; j < methods.length; j++) {
				String iName = methods[i].getName();
				String jName = methods[j].getName();

				if (iName.compareTo(jName) > 0) {
					Method temp = methods[i];
					methods[i] = methods[j];
					methods[j] = temp;
				}
			}
		}

		JSONObject jObj = new JSONObject();
		JSONArray jArray = new JSONArray();
		for (Method method : methods) {
			JSONObject obj = new JSONObject();
			obj.put("methodName", method.getName());
			obj.put("returnType", method.getReturnType().getSimpleName());
			JSONArray parameters = new JSONArray();
			Class[] parameterTypes = method.getParameterTypes();
			String params = "";
			for (Class parameter : parameterTypes) {
				params += ", " + parameter.getSimpleName();
				parameters.add(parameter.getSimpleName());
			}
			if (params.length() > 0) {
				params = params.substring(2);
			}
			obj.put("label", method.getName() + "(" + params + ") : " + method.getReturnType().getSimpleName());
			obj.put("parameters", parameters);
			jArray.add(obj);
		}

		jObj.put("identifier", "label");
		jObj.put("label", "label");
		jObj.put("items", jArray);
		json = jObj.toJSONString();

		return json;
	}

	private String getDataType(String code, int targetLineNumber, String variableName) throws IOException, ClassNotFoundException {
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);

		Iterable<? extends JavaFileObject> compilationUnits1 = getJavaSourceFromString(code);
		CompilationTask task = compiler.getTask(null, fileManager, null, null, null, compilationUnits1);
		JavacTask javacTask = (JavacTask) task;
		Iterable<? extends CompilationUnitTree> units = javacTask.parse();

		CodeAnalyzerTreeVisitor visitor = new CodeAnalyzerTreeVisitor(targetLineNumber, variableName, false);
		Trees trees = Trees.instance(task);
		for (CompilationUnitTree cut : units) {
			TreePath treePath = new TreePath(cut);
			visitor.scan(treePath, trees);
		}

		String dataType = visitor.getType();

		try {
			fileManager.close();
		} catch (IOException e) {
			// System.out.println(e.getLocalizedMessage());
		}
		return dataType;
	}

	static Iterable<JavaSourceFromString> getJavaSourceFromString(String code) {
		final JavaSourceFromString jsfs;
		jsfs = new JavaSourceFromString("Test", code);
		return new Iterable<JavaSourceFromString>() {
			public Iterator<JavaSourceFromString> iterator() {
				return new Iterator<JavaSourceFromString>() {
					boolean isNext = true;

					public boolean hasNext() {
						return isNext;
					}

					public JavaSourceFromString next() {
						if (!isNext)
							throw new NoSuchElementException();
						isNext = false;
						return jsfs;
					}

					public void remove() {
						throw new UnsupportedOperationException();
					}
				};
			}
		};
	}
}
